﻿using System.Diagnostics;
using System.Reflection;
using System.Runtime.InteropServices;
using RDF;
using System.Windows.Forms;
using System.Drawing;

using stepengine = RDF.ifcengine;

#if _WIN64
#else
using int_t = System.Int32;
#endif

namespace DZJIFC.Model
{
    public class AP242ModelStructureView : IModelStructureView
    {
        #region Fields

        IController _controller;
        AP242Model? _model;
        TreeView _treeView;
        ContextMenuStrip _contextMenuStrip;
        TreeNode? _previousSelectedNode;
        Dictionary<AP242Product, AP242InstancesIterator> _dicInstanceIterators;

        #endregion // Fields

        #region Methods

        public AP242ModelStructureView(IController controller, TreeView treeView, ContextMenuStrip contextMenuStrip)
        {
            if ((controller == null) || 
                (treeView == null) || 
                (contextMenuStrip == null))
            {
                throw new ArgumentNullException();
            }

            _controller = controller;
            _model = controller.Model as AP242Model;
            _contextMenuStrip = contextMenuStrip;

            _treeView = treeView;
            _contextMenuStrip = contextMenuStrip;
        }

        #region IModelStructureView

        public void OnModelLoaded()
        {
            Debug.Assert(_controller.Model != null);

            _treeView.Nodes.Clear();
            _previousSelectedNode = null;
            _dicInstanceIterators = new Dictionary<AP242Product, AP242InstancesIterator>();

            _model = _controller.Model as AP242Model;
            if (_model != null && !string.IsNullOrEmpty(_model.FilePath))
            {
                // Model
                var tnModel = _treeView.Nodes.Add(_model.FilePath);
                tnModel.ContextMenuStrip = _contextMenuStrip;
                tnModel.ImageIndex = tnModel.SelectedImageIndex = IModelStructureView.IMAGE_CHECKED;
                tnModel.Tag = new AP242ItemData(_model, AP242ItemType.Model, tnModel);

                LoadHeader(tnModel);
                LoadModel(tnModel);

                tnModel.Expand();

                // Check children (descendants) for Geometry
                foreach (var node in _treeView.Nodes)
                {
                    var tnItem = node as TreeNode;
                    if (tnItem.Tag != null)
                    {
                        UpdateNodesState(tnItem.Tag as AP242ItemData);
                    }
                }
            }
        }

        private void UpdateNodesState(AP242ItemData itemData)
        {
            Debug.Assert(itemData != null);

            if (itemData.Type == AP242ItemType.Geometry)
            {
                // Ignore
                return;
            }

            bool bHasDescendantWithGeometry = false;

            // Product
            if (itemData.Type == AP242ItemType.Product)
            {
                var product = itemData.Instance as AP242Product;
                Debug.Assert(product != null);

                bHasDescendantWithGeometry |= product.HasGeometry;
            }

            // Instance
            if (!bHasDescendantWithGeometry)
            {
                if (itemData.Type == AP242ItemType.Instance)
                {
                    var instance = itemData.Instance as AP242Instance;
                    Debug.Assert(instance != null);

                    bHasDescendantWithGeometry |= instance.Geometry.HasGeometry;
                }
            }

            foreach (var node in itemData.TreeNode.Nodes)
            {
                var tnChild = node as TreeNode;

                var childItemData = tnChild.Tag as AP242ItemData;
                if (childItemData != null)
                {
                    if (childItemData.Type == AP242ItemType.Geometry)
                    {
                        // Ignore
                        continue;
                    }

                    // Product
                    if (!bHasDescendantWithGeometry)
                    {
                        if (childItemData.Type == AP242ItemType.Product)
                        {
                            var product = childItemData.Instance as AP242Product;
                            Debug.Assert(product != null);

                            bHasDescendantWithGeometry |= product.HasGeometry;
                        }
                    }

                    // Instance
                    if (!bHasDescendantWithGeometry)
                    {
                        if (childItemData.Type == AP242ItemType.Instance)
                        {
                            var instance = childItemData.Instance as AP242Instance;
                            Debug.Assert(instance != null);

                            bHasDescendantWithGeometry |= instance.Geometry.HasGeometry;
                        }
                    }

                    if (!bHasDescendantWithGeometry)
                    {
                        UpdateNodesStateRecursivelly(childItemData, ref bHasDescendantWithGeometry);
                    }

                    UpdateNodesState(childItemData);
                } // if (childItemData != null)
            } // foreach (var node in ...

            if (!bHasDescendantWithGeometry)
            {
                itemData.TreeNode.ImageIndex = itemData.TreeNode.SelectedImageIndex = IModelStructureView.IMAGE_NO_GEOMETRY;
            }
        }

        private void UpdateNodesStateRecursivelly(AP242ItemData itemData, ref bool bHasDescendantWithGeometry)
        {
            Debug.Assert(itemData != null);

            foreach (var node in itemData.TreeNode.Nodes)
            {
                var tnChild = node as TreeNode;

                var childItemData = tnChild.Tag as AP242ItemData;
                if (childItemData != null)
                {
                    if (childItemData.Type == AP242ItemType.Geometry)
                    {
                        // Ignore
                        continue;
                    }

                    // Product
                    if (childItemData.Type == AP242ItemType.Product)
                    {
                        var product = childItemData.Instance as AP242Product;
                        Debug.Assert(product != null);

                        bHasDescendantWithGeometry |= product.HasGeometry;
                        if (bHasDescendantWithGeometry)
                        {
                            return;
                        }
                    }

                    // Instance
                    if (childItemData.Type == AP242ItemType.Instance)
                    {
                        var instance = childItemData.Instance as AP242Instance;
                        Debug.Assert(instance != null);

                        bHasDescendantWithGeometry |= instance.Geometry.HasGeometry;
                        if (bHasDescendantWithGeometry)
                        {
                            return;
                        }
                    }

                    UpdateNodesStateRecursivelly(childItemData, ref bHasDescendantWithGeometry);
                    if (bHasDescendantWithGeometry)
                    {
                        return;
                    }
                } // if (childItemData != null)
            } // foreach (var node in ...
        }

        public void OnInstanceSelected(object sender, IInstance instance)
        {
            if (sender == this)
            {
                return;
            }

            if (instance != null && instance.UIItem != null)
            {
                var itemData = instance.UIItem as AP242ItemData;
                Debug.Assert(itemData != null);

                _treeView.SelectedNode = itemData.TreeNode;
            }
            else
            {
                _treeView.SelectedNode = null;

                if (_previousSelectedNode != null)
                {
                    _previousSelectedNode.BackColor = _treeView.BackColor;
                }

                _previousSelectedNode = null;
            }
        }

        public void TreeView_OnNodeMouseClick(object sender, TreeNodeMouseClickEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                Rectangle rcIcon = new Rectangle(e.Node.Bounds.Location - new Size(16, 0), new Size(16, 16));
                if (!rcIcon.Contains(e.Location))
                {
                    return;
                }

                var itemData = e.Node.Tag as AP242ItemData;
                Debug.Assert(itemData != null);

                switch (e.Node.ImageIndex)
                {
                    case IModelStructureView.IMAGE_CHECKED:
                    case IModelStructureView.IMAGE_MIDDLE:
                        {
                            e.Node.ImageIndex = e.Node.SelectedImageIndex = IModelStructureView.IMAGE_UNCHECKED;

                            // Geometry
                            if (itemData.Type == AP242ItemType.Geometry)
                            {
                                Debug.Assert(itemData.TreeNode.Parent != null);

                                var parentItemData = itemData.TreeNode.Parent.Tag as AP242ItemData;
                                Debug.Assert(parentItemData.Item != null);

                                parentItemData.Item.Enabled = false;

                                OnChildItemStateChanged(itemData.TreeNode);
                            }
                            else
                            {
                                if (itemData.Item != null)
                                {
                                    itemData.Item.Enabled = false;
                                }

                                OnParentItemStateChanged(itemData.TreeNode);
                                OnChildItemStateChanged(itemData.TreeNode);
                            }
                        }
                        break;

                    case IModelStructureView.IMAGE_UNCHECKED:
                        {
                            e.Node.ImageIndex = e.Node.SelectedImageIndex = IModelStructureView.IMAGE_CHECKED;

                            if (itemData.Type == AP242ItemType.Geometry)
                            {
                                Debug.Assert(itemData.TreeNode.Parent != null);

                                var parentItemData = itemData.TreeNode.Parent.Tag as AP242ItemData;
                                Debug.Assert(parentItemData.Item != null);

                                parentItemData.Item.Enabled = true;

                                OnChildItemStateChanged(itemData.TreeNode);
                            }
                            else
                            {
                                if (itemData.Item != null)
                                {
                                    itemData.Item.Enabled = true;
                                }

                                OnParentItemStateChanged(itemData.TreeNode);
                                OnChildItemStateChanged(itemData.TreeNode);
                            }
                        }
                        break;
                } // switch (e.Node.ImageIndex)
            } // if (e.Button == MouseButtons.Left)
            else
            {
                if (e.Button == MouseButtons.Right)
                {
                    _treeView.SelectedNode = e.Node;
                }
            }
        }

        private void OnChildItemStateChanged(TreeNode tnChild)
        {
            Debug.Assert(tnChild != null);

            if (tnChild.Parent == null)
            {
                return;
            }

            int iItemsCount = 0;
            int iCheckedItems = 0;
            int iSemiCheckedItems = 0;
            foreach (var node in tnChild.Parent.Nodes)
            {
                var treeNode = node as TreeNode;
                if (treeNode?.Tag == null)
                {
                    continue;
                }

                var itemData = treeNode.Tag as AP242ItemData;
                Debug.Assert(itemData != null);

                iItemsCount += itemData.TreeNode.ImageIndex == 
                    IModelStructureView.IMAGE_CHECKED || itemData.TreeNode.ImageIndex == IModelStructureView.IMAGE_MIDDLE || itemData.TreeNode.ImageIndex == IModelStructureView.IMAGE_UNCHECKED 
                    ? 1 : 0;
                iCheckedItems += itemData.TreeNode.ImageIndex == IModelStructureView.IMAGE_CHECKED ? 1 : 0;
                iSemiCheckedItems += itemData.TreeNode.ImageIndex == IModelStructureView.IMAGE_MIDDLE ? 1 : 0;
            } // foreach (var treeNode ...

            // Update the state only if the item has children; otherwise keep the original state
            if (iItemsCount > 0)
            {
                if (iSemiCheckedItems > 0)
                {
                    tnChild.Parent.ImageIndex = tnChild.Parent.SelectedImageIndex = IModelStructureView.IMAGE_MIDDLE;
                }
                else
                {
                    if (iCheckedItems > 0)
                    {
                        tnChild.Parent.ImageIndex = tnChild.Parent.SelectedImageIndex = 
                            iCheckedItems == iItemsCount ? 
                            IModelStructureView.IMAGE_CHECKED : 
                            IModelStructureView.IMAGE_MIDDLE;
                    }
                    else
                    {
                        tnChild.Parent.ImageIndex = tnChild.Parent.SelectedImageIndex = IModelStructureView.IMAGE_UNCHECKED;
                    }
                }
            } // if (iItemsCount > 0)

            if (tnChild.Parent.Parent != null)
            {
                OnChildItemStateChanged(tnChild.Parent);
            }
        }

        private void OnParentItemStateChanged(TreeNode tnParent)
        {
            Debug.Assert(tnParent != null);

            foreach (var node in tnParent.Nodes)
            {
                var treeNode = node as TreeNode;
                if (treeNode?.Tag == null)
                {
                    continue;
                }

                var itemData = treeNode.Tag as AP242ItemData;
                Debug.Assert(itemData != null);

                if (itemData.TreeNode.ImageIndex == IModelStructureView.IMAGE_NO_GEOMETRY)
                {
                    // Keep the image

                    if (itemData.Item != null)
                    {
                        itemData.Item.Enabled = false;
                    }

                    // Do not update children
                    continue;
                }

                switch (tnParent.ImageIndex)
                {
                    case IModelStructureView.IMAGE_CHECKED:
                        {
                            itemData.TreeNode.ImageIndex = itemData.TreeNode.SelectedImageIndex = IModelStructureView.IMAGE_CHECKED;

                            if (itemData.Item != null)
                            {
                                itemData.Item.Enabled = true;
                            }
                        }
                        break;

                    case IModelStructureView.IMAGE_UNCHECKED:
                        {
                            itemData.TreeNode.ImageIndex = itemData.TreeNode.SelectedImageIndex = IModelStructureView.IMAGE_UNCHECKED;

                            if (itemData.Item != null)
                            {
                                itemData.Item.Enabled = false;
                            }
                        }
                        break;

                    default:
                        {
                            Debug.Assert(false); // unexpected
                        }
                        break;
                } // switch (ifcParent.ImageIndex)

                OnParentItemStateChanged(itemData.TreeNode);
            } // foreach (var treeNode ...
        }

        public void TreeView_AfterSelect(object sender, TreeViewEventArgs e)
        {
            if (e.Node?.Tag != null)
            {
                var itemData = e.Node.Tag as AP242ItemData;
                if (itemData == null)
                {
                    throw new Exception("Internal error.");
                }

                if (itemData.Type == AP242ItemType.Instance)
                {
                    _controller.SelectInstance(this, itemData.Instance as AP242Instance);
                }

                if (_previousSelectedNode != null)
                {
                    _previousSelectedNode.BackColor = _treeView.BackColor;
                }

                _previousSelectedNode = e.Node;

                e.Node.BackColor = Color.Red;
            }
        }

        public void TreeView_BeforeExpand(object sender, TreeViewCancelEventArgs e)
        {
        }

        public IInstance GetSelectedInstance()
        {
            var selectedNode = _treeView.SelectedNode;
            if (selectedNode.Tag != null)
            {
                var itemData = selectedNode.Tag as AP242ItemData;
                Debug.Assert(itemData != null);

                if (itemData.Type == AP242ItemType.Instance)
                {
                    return itemData.Instance as IInstance;
                }
            }

            return null;
        }

        #endregion // IModelStructureView

        private void LoadHeader(TreeNode tnModel)
        {
            // Header
            var tnHeader = tnModel.Nodes.Add("Header");
            tnHeader.ImageIndex = tnHeader.SelectedImageIndex = IModelStructureView.IMAGE_PROPERTY_SET;

            // Descriptions
            var tnDescriptions = tnHeader.Nodes.Add("Descriptions");
            tnDescriptions.ImageIndex = tnDescriptions.SelectedImageIndex = IModelStructureView.IMAGE_PROPERTY_SET;

            int i = 0;
            while (stepengine.GetSPFFHeaderItem(_model.Instance, 0, i++, stepengine.sdaiUNICODE, out IntPtr description) == 0)
            {
                var tnDescription = tnDescriptions.Nodes.Add(Marshal.PtrToStringUni(description));
                tnDescription.ImageIndex = tnDescription.SelectedImageIndex = IModelStructureView.IMAGE_PROPERTY;
            }

            // ImplementationLevel
            stepengine.GetSPFFHeaderItem(_model.Instance, 1, 0, stepengine.sdaiUNICODE, out IntPtr implementationLevel);

            var tnImplementationLevel = tnHeader.Nodes.Add("ImplementationLevel = '" + Marshal.PtrToStringUni(implementationLevel) + "'");
            tnImplementationLevel.ImageIndex = tnImplementationLevel.SelectedImageIndex = IModelStructureView.IMAGE_PROPERTY;

            // Name
            stepengine.GetSPFFHeaderItem(_model.Instance, 2, 0, stepengine.sdaiUNICODE, out IntPtr name);

            var tnName = tnHeader.Nodes.Add("Name = '" + Marshal.PtrToStringUni(name) + "'");
            tnName.ImageIndex = tnName.SelectedImageIndex = IModelStructureView.IMAGE_PROPERTY;

            // TimeStamp
            stepengine.GetSPFFHeaderItem(_model.Instance, 3, 0, stepengine.sdaiUNICODE, out IntPtr timeStamp);

            var tnTimeStamp = tnHeader.Nodes.Add("TimeStamp = '" + Marshal.PtrToStringUni(timeStamp) + "'");
            tnTimeStamp.ImageIndex = tnTimeStamp.SelectedImageIndex = IModelStructureView.IMAGE_PROPERTY;

            // Authors
            var tnAuthors = tnHeader.Nodes.Add("Authors");
            tnAuthors.ImageIndex = tnAuthors.SelectedImageIndex = IModelStructureView.IMAGE_PROPERTY_SET;

            i = 0;
            while (stepengine.GetSPFFHeaderItem(_model.Instance, 4, i++, stepengine.sdaiUNICODE, out IntPtr author) == 0)
            {
                var tnAuthor = tnAuthors.Nodes.Add(Marshal.PtrToStringUni(author));
                tnAuthor.ImageIndex = tnAuthor.SelectedImageIndex = IModelStructureView.IMAGE_PROPERTY;
            }

            // Organizations
            var tnOrganizations = tnHeader.Nodes.Add("Organizations");
            tnOrganizations.ImageIndex = tnOrganizations.SelectedImageIndex = IModelStructureView.IMAGE_PROPERTY_SET;

            i = 0;
            while (stepengine.GetSPFFHeaderItem(_model.Instance, 5, i++, stepengine.sdaiUNICODE, out IntPtr organization) == 0)
            {
                var tnOrganization = tnOrganizations.Nodes.Add(Marshal.PtrToStringUni(organization));
                tnOrganization.ImageIndex = tnOrganization.SelectedImageIndex = IModelStructureView.IMAGE_PROPERTY;
            }

            // PreprocessorVersion
            stepengine.GetSPFFHeaderItem(_model.Instance, 6, 0, stepengine.sdaiUNICODE, out IntPtr preprocessorVersion);

            var tnPreprocessorVersion = tnHeader.Nodes.Add("PreprocessorVersion = '" + Marshal.PtrToStringUni(preprocessorVersion) + "'");
            tnPreprocessorVersion.ImageIndex = tnPreprocessorVersion.SelectedImageIndex = IModelStructureView.IMAGE_PROPERTY;

            // OriginatingSystem
            stepengine.GetSPFFHeaderItem(_model.Instance, 7, 0, stepengine.sdaiUNICODE, out IntPtr originatingSystem);

            var tnOriginatingSystem = tnHeader.Nodes.Add("OriginatingSystem = '" + Marshal.PtrToStringUni(originatingSystem) + "'");
            tnOriginatingSystem.ImageIndex = tnOriginatingSystem.SelectedImageIndex = IModelStructureView.IMAGE_PROPERTY;

            // Authorization
            stepengine.GetSPFFHeaderItem(_model.Instance, 8, 0, stepengine.sdaiUNICODE, out IntPtr authorization);

            var tnAuthorization = tnHeader.Nodes.Add("Authorization = '" + Marshal.PtrToStringUni(authorization) + "'");
            tnAuthorization.ImageIndex = tnAuthorization.SelectedImageIndex = IModelStructureView.IMAGE_PROPERTY;

            // FileSchemas
            var tnFileSchemas = tnHeader.Nodes.Add("FileSchemas");
            tnFileSchemas.ImageIndex = tnFileSchemas.SelectedImageIndex = IModelStructureView.IMAGE_PROPERTY_SET;

            i = 0;
            while (stepengine.GetSPFFHeaderItem(_model.Instance, 9, i++, stepengine.sdaiUNICODE, out IntPtr fileSchema) == 0)
            {
                var tnFileSchema = tnFileSchemas.Nodes.Add(Marshal.PtrToStringUni(fileSchema));
                tnFileSchema.ImageIndex = tnFileSchema.SelectedImageIndex = IModelStructureView.IMAGE_PROPERTY;
            }
        }

        private void LoadModel(TreeNode tnModel)
        {
            // Roots
            foreach (var product in _model.ExpressID2Product.Values)
            {
                if (product.RelatedProductsCount == 0)
                {
                    LoadProduct(tnModel, product);
                }
            }
        }

        private void LoadProduct(TreeNode tnParent, AP242Product product)
        {
            if (_model ==  null)
            {
                return;
            }

            // Inststance
            var instance = GetNextInstance(product);
            if (instance == null)
            {
                throw new Exception("Internal error");
            }

            // Product
            var tnProduct = tnParent.Nodes.Add(product.UniqueName);
            tnProduct.ContextMenuStrip = _contextMenuStrip;
            tnProduct.ImageIndex = tnProduct.SelectedImageIndex =
                product.HasGeometry || HasDescendantsWithGeometry(product) ? 
                IModelStructureView.IMAGE_CHECKED : 
                IModelStructureView.IMAGE_NO_GEOMETRY;
            tnProduct.Tag = new AP242ItemData(instance, AP242ItemType.Instance, tnProduct);
            instance.UIItem = (IUIItem)tnProduct.Tag;
            ((AP242ItemData)tnProduct.Tag).Item = instance;

            // Geometry
            var tnGeometry = tnProduct.Nodes.Add(IModelStructureView.GEOMETRY);
            tnGeometry.ImageIndex = tnGeometry.SelectedImageIndex = product.HasGeometry ?
                IModelStructureView.IMAGE_CHECKED :
                IModelStructureView.IMAGE_NO_GEOMETRY;
            tnGeometry.Tag = new AP242ItemData(instance, AP242ItemType.Geometry, tnGeometry);

            // Assemblies
            foreach (var assembly in _model.ExpressID2Assembly.Values)
            {
                if (assembly.RelatingProduct == product)
                {
                    LoadAssembly(tnProduct, assembly);
                }
            }
        }

        private void LoadAssembly(TreeNode tnParent, AP242Assembly assembly)
        {
            Debug.Assert(tnParent != null);
            Debug.Assert(assembly != null);

            // Assembly
            var tnAssembly = tnParent.Nodes.Add(assembly.UniqueName);
            tnAssembly.ContextMenuStrip = _contextMenuStrip;
            tnAssembly.ImageIndex = tnAssembly.SelectedImageIndex =
                HasDescendantsWithGeometry(assembly) ? 
                IModelStructureView.IMAGE_CHECKED :
                IModelStructureView.IMAGE_NO_GEOMETRY;
            tnAssembly.Tag = new AP242ItemData(assembly, AP242ItemType.Assembly, tnAssembly);

            // Geometry
            var tnGeometry = tnAssembly.Nodes.Add(IModelStructureView.GEOMETRY);
            tnGeometry.ImageIndex = tnGeometry.SelectedImageIndex = IModelStructureView.IMAGE_NO_GEOMETRY;
            tnGeometry.Tag = new AP242ItemData(assembly, AP242ItemType.Geometry, tnGeometry);

            // Product
            LoadProduct(tnAssembly, assembly.RelatedProduct);
        }

        private void LoadInstance(TreeNode tnParent, AP242Instance instance)
        {
            Debug.Assert(tnParent != null);
            Debug.Assert(instance != null);

            // Instance
            var tnInstance = tnParent.Nodes.Add(instance.UniqueName);
            tnInstance.ContextMenuStrip = _contextMenuStrip;
            tnInstance.ImageIndex = tnInstance.SelectedImageIndex = IModelStructureView.IMAGE_CHECKED;
            tnInstance.Tag = new AP242ItemData(instance, AP242ItemType.Instance, tnInstance);
            instance.UIItem = (IUIItem)tnInstance.Tag;
            ((AP242ItemData)tnInstance.Tag).Item = instance;

            // Geometry
            var tnGeometry = tnInstance.Nodes.Add(IModelStructureView.GEOMETRY);
            tnGeometry.ImageIndex = tnGeometry.SelectedImageIndex = instance.Geometry.HasGeometry ?
                IModelStructureView.IMAGE_CHECKED : IModelStructureView.IMAGE_NO_GEOMETRY;
            tnGeometry.Tag = new AP242ItemData(instance, AP242ItemType.Geometry, tnGeometry);
        }

        private AP242Instance? GetNextInstance(AP242Product product)
        {
            if (product == null)
            {
                throw new ArgumentNullException();
            }

            if (!_dicInstanceIterators.ContainsKey(product))
            {
                var iterator = new AP242InstancesIterator(product);
                _dicInstanceIterators[product] = iterator;

                return iterator.GetNextInstance();
            }

            return _dicInstanceIterators[product].GetNextInstance();
        }

        private bool HasDescendantsWithGeometry(AP242Product product)
        {
            if (product == null)
            {
                throw new ArgumentNullException();
            }

            if (product.HasGeometry)
            {
                return true;
            }

            bool bHasDescendantsWithGeometry = false;
            HasDescendantsWithGeometryRecursively(product, ref bHasDescendantsWithGeometry);

            return bHasDescendantsWithGeometry;
        }

        private void HasDescendantsWithGeometryRecursively(AP242Product product, ref bool bHasDescendantsWithGeometry)
        {
            if (product == null)
            {
                throw new ArgumentNullException();
            }

            foreach (var assembly in _model.ExpressID2Assembly.Values)
            {
                if (assembly.RelatingProduct == product)
                {
                    bHasDescendantsWithGeometry = HasDescendantsWithGeometry(assembly.RelatedProduct);
                    if (bHasDescendantsWithGeometry)
                    {
                        break;
                    }
                }
            }
        }

        private bool HasDescendantsWithGeometry(AP242Assembly assembly)
        {
            if (assembly == null)
            {
                throw new ArgumentNullException();
            }

            return HasDescendantsWithGeometry(assembly.RelatedProduct);
        }

        #endregion // Methods
    }
}
